/*
 * @Author       : xiangmin
 * @File         : 表格统一处理
 * @Date         : 2023-06-30 17:41:10
 * @LastEditors  : xiangmin
 * @LastEditTime : 2024-06-18 19:12:55
 */

import { useHomeStore } from '@/store'
import home from '@/store/home'
import { useDocumentVisibility } from '@vueuse/core'
import type { PaginationProps } from 'ant-design-vue'
import { remove } from 'lodash-es'
import { storeToRefs } from 'pinia'
import type { RequestConfig } from './request'

// 表格复选框类型
type Key = number | string
type Selected<T> = {
	keys: Key[]
	rows: T[]
}

type Sorter = {
	field: string
	order: 'ascend' | 'descend'
	[key: string]: any
}

interface UseTableConfig<T, R> extends RequestConfig {
	// 表格操作是类型
	type?: 'radio' | 'checkbox'
	// 分页复选框选择时是否记住以前的勾选项
	keepChecked?: boolean
	// 初始化时是否立即发送请求
	immediate?: boolean
	// 表格数据的唯一标识项，例如数据的id等
	rowKey?: string
	// 分页初始化页码
	current?: number
	// 分页初始化大小
	pageSize?: number
	// 分页size大小配置
	pageSizeOptions?: string[] | number[]
	// 分页的各种选项
	paginationOptions?: PaginationProps
	// 当页面的visible重新变化为false重新触发请求
	refreshPageOnShow?: boolean
	// visible变化时触发请求(默认为5000ms)
	refreshTimespan?: number
	// 点击表格复选框的回调
	onSelectChangeCallback?: (select: Selected<T>) => void
	// 格式化数据
	formater?: (data: R) => R
	// 表格请求成共后的回调
	onSuccess?: (list: T[], data: R) => void
	// 失败后的回调
	onError?: (error: Record<string, any>) => void
	// 请求参数params处理，并且返回最终需要处理的参数，或者返回一个Promis.reject阻止请求继续进行
	onBefore?: (args: {
		params: Record<string, any>
		sorter: Sorter
		filter: Record<string, any>
		pagination: PaginationProps
	}) => Promise<any> | Record<string, any> | void
	// 异步最后的回调
	onFinally?: () => void
}

/**
 * 表格组件的使用函数
 * @param url - 请求接口的URL
 * @param type - 表格选择类型，可选值为 'checkbox' 或 'radio'
 * @param keepChecked - 是否保持选择状态，当type为 'radio' 时有效
 * @param config - 请求配置项
 * @param rowKey - 行key字段名，当type为 'checkbox' 且 keepChecked 为 false 时需要提供
 * @param immediate - 是否立即请求数据
 * @param params - 请求参数
 * @param current - 当前页码
 * @param refreshTimespan - 刷新时间间隔
 * @param refreshPageOnShow - 是否在页面可见时刷新数据
 * @param pageSize - 每页显示数量，默认为 home 组件的 size 属性值
 * @param pageSizeOptions - 可选的每页显示数量选项，默认为 [10, 15, 20, 30, 40]
 * @param paginationOptions - 分页选项
 * @param onSelectChangeCallback - 选择行变化回调函数
 * @param formater - 格式化请求结果
 * @param onSuccess - 请求成功回调函数
 * @param onError - 请求错误回调函数
 * @param onBefore - 请求前的回调函数
 * @param onFinally - 请求结束后的回调函数
 */
function useTable<T, R extends TableData<T> = TableData<T>>({
	url,
	type = 'checkbox',
	keepChecked = true,
	config,
	rowKey,
	immediate = true,
	params = {},
	current = 1,
	refreshTimespan = 2000,
	refreshPageOnShow = false,
	pageSize = useHomeStore().size,
	pageSizeOptions = ['10', '15', '20', '30', '40'],
	paginationOptions = {},
	onSelectChangeCallback,
	formater,
	onSuccess,
	onError,
	onBefore,
	onFinally
}: UseTableConfig<T, R>) {
	const loading = ref(false)
	const list = ref([]) as Ref<T[]>
	const data = ref({}) as Ref<R>
	const error = ref<Record<string, any> | null>(null)
	const initParams = cloneDeep(params)
	let isPageChange = false
	// 表格表头的排序、筛选参数
	let sorter: Sorter = {} as Sorter
	let filter: Record<string, any> = {}
	const pagination = reactive({
		current,
		total: 0,
		// 默认每页显示数量
		pageSize,
		// 显示可改变每页数量
		showSizeChanger: true,
		// 每页数量选项
		pageSizeOptions,
		showTotal: total => `共 ${total} 条`,
		onChange(page, pageSize) {
			isPageChange = true
			pagination.pageSize = pageSize
			pagination.current = page
			fetchList()
		},
		...paginationOptions
	})

	async function fetchList() {
		let finallyParams = params
		if (onBefore) {
			const onBeforeParams = await onBefore({
				params: cloneDeep(params),
				sorter,
				filter,
				pagination
			})
			onBeforeParams && (finallyParams = onBeforeParams)
		}
		loading.value = true
		get<R>({
			url,
			params: {
				limit: pagination.pageSize,
				page: pagination.current,
				...finallyParams
			},
			config
		})
			.then(response => {
				response = formater ? formater(cloneDeep(response)) : response
				loading.value = false
				data.value = response
				// @ts-ignore
				list.value = response
				// pagination.current = response.page
				pagination.total = 10000
				// @ts-ignore
				onSuccess && onSuccess(response, response)
			})
			.catch(err => {
				error.value = err
				err && (loading.value = false)
				onError && onError(err)
			})
			.finally(() => onFinally && onFinally())
	}

	immediate && fetchList()

	function reset() {
		pagination.pageSize = pageSize
		pagination.current = current
		Object.assign(params, cloneDeep(initParams))
		clearSelected()
		fetchList()
	}

	function query() {
		pagination.current = current
		clearSelected()
		fetchList()
	}

	// 表格复选框操作选中的行和id
	const selected: Selected<T> = shallowReactive({
		keys: [],
		rows: []
	})
	function onSelectChange(keys: Key[], rows: T[]): void {
		if (type === 'radio' || !keepChecked) {
			selected.keys = keys
			selected.rows = rows
		} else {
			if (!rowKey) {
				throw new Error('rowKey 不能为空，并且要和a-table的rowKey一样的')
			}
			const listIds: Key[] = list.value.map(item => item[rowKey])
			remove(selected.keys, id => listIds.includes(id))
			remove(selected.rows, item => listIds.includes(item[rowKey]))
			selected.keys = selected.keys.concat(keys)
			selected.rows = selected.rows.concat(rows)
		}
		onSelectChangeCallback && onSelectChangeCallback(selected)
	}
	function clearSelected() {
		selected.keys = []
		selected.rows = []
	}

	// 表格排序/过滤的回调
	function onTableChange(_, filters, sort) {
		if (isPageChange) {
			isPageChange = false
			return
		}
		filter = filters
		sorter = sort
		pagination.current = 1
		fetchList()
	}

	// 监听全局分页设置变化时刷新页面
	const { size } = storeToRefs(home())
	watch(size, () => {
		pagination.pageSize = size.value
		fetchList()
	})

	function onRefresh() {
		// 监听页面visible来刷新页面
		let timespan = new Date().getTime()
		const visiblity = useDocumentVisibility()
		watch(visiblity, value => {
			if (value === 'visible' && new Date().getTime() - timespan > refreshTimespan) {
				fetchList()
				timespan = new Date().getTime()
			}
		})
	}
	refreshPageOnShow && onRefresh()

	return {
		error,
		list,
		data,
		loading,
		pagination,
		selected,
		fetchList,
		reset,
		query,
		clearSelected,
		onSelectChange,
		onTableChange
	}
}

export { useTable }
